#pragma once

#include <sys/select.h>
#include <iostream>
#include <string>
#include <algorithm>
#include "Socket.hpp"

static const int max_fd_cnt = sizeof(fd_set) * 8;

class SelectServer
{
public:
    void Init()
    {
        _listenSock.Sock();
        _listenSock.Bind(9999);
        _listenSock.Listen();
        for(int i = 0; i < max_fd_cnt; ++i) {
            fds[i] = -1;
        }
        fds[0] = _listenSock.Fd();
    }

    void Accepter()
    {
        //listen fd就绪
        std::string clientip; uint16_t clientPort;
        int newFd = _listenSock.Accept(&clientip, &clientPort);
        std::cerr << "get a new fd, fd: " << newFd << std::endl;
        for(int j = 0; j < max_fd_cnt; ++j) {
            if(fds[j] == -1) { fds[j] = newFd; break; }
        }
    }

    void Recver(fd_set& read_fds, int pos)
    {
        //普通fd就绪
        char buff[1024] = { 0 };
        ssize_t n = read(fds[pos], buff, sizeof(buff) - 1);
        if(n > 0) {
            buff[n] = 0;
            std::cout << "echo# " << buff << std::endl;
        }
        else if(n < 0) { std::cerr << "read err" << std::endl; }
        else {
            //断开连接
            int oldfd = fds[pos];
            FD_CLR(oldfd, &read_fds);
            fds[pos] = -1;
            close(fds[pos]);
            std::cerr << "close fd, fd: " << oldfd << std::endl;
        }
    }

    void prinFds()
    {
        for(int i = 0; i < 10; ++i) {
            std::cerr << fds[i] << " ";
        }
        std::cerr << std::endl;
    }

    void Dispatcher(fd_set& read_fds)
    {
        for(int i = 0; i < max_fd_cnt; ++i)
        {
            if(fds[i] == -1) { continue; }
            if(FD_ISSET(fds[i], &read_fds))
            {
                std::cerr << "Dispatcher fd:" << fds[i] << std::endl;
                if(fds[i] == _listenSock.Fd()) {
                    Accepter();
                }
                else {
                    Recver(read_fds, i);
                }
            }
        }
    }

    void Run()
    {
        int listenfd = _listenSock.Fd();
        fd_set read_fds;
        while(true)
        {
            FD_ZERO(&read_fds);
            // prinFds();
            int maxFd = -1;
            for(int i = 0; i < max_fd_cnt; ++i) {
                if(fds[i] != -1) {
                    FD_SET(fds[i], &read_fds); // 用户层fds -> 内核结构fd_set
                    maxFd = std::max(maxFd, fds[i]);
                }
            }
            timeval time; bzero(&time, sizeof(time));
            time.tv_sec = 2; time.tv_usec = 0;
            /*
            After select() has returned, *fds will be cleared of all file descriptors except for those that are ready
            for reading.
            */
            int ret = select(maxFd + 1, &read_fds, nullptr, nullptr, &time);
            
            if(ret == 0) {
                std::cerr << "time out" << std::endl;
            }
            else if(ret < 0) {
                std::cerr << "select err" << std::endl;
            }
            else {
                // std::cerr << "get new links" << std::endl;
                Dispatcher(read_fds);
            }
        }
    }

private:
    Socket _listenSock;
    int fds[max_fd_cnt];
};
